/*
 * SPDX-License-Identifier: BSD-3-Clause
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. All rights reserved.
 * Description   : udk ether interface
 */

#ifndef UDK_ETHER_H
#define UDK_ETHER_H

#include <stdint.h>
#include <stddef.h>

#include "udk_byteorder.h"
#include "udk_common.h"

#define UDK_ETHER_ADDR_LEN  6 /* Length of Ethernet address. */
#define UDK_ETHER_TYPE_LEN  2 /* Length of Ethernet type field. */
#define UDK_ETHER_CRC_LEN   4 /* Length of Ethernet CRC. */
#define UDK_ETHER_HDR_LEN   (UDK_ETHER_ADDR_LEN * 2 + UDK_ETHER_TYPE_LEN) /* Length of Ethernet header. */
#define UDK_ETHER_MIN_LEN   64    /* Minimum frame len, including CRC. */
#define UDK_ETHER_MAX_LEN   1518  /* Maximum frame len, including CRC. */
#define UDK_ETHER_MTU       ((UDK_ETHER_MAX_LEN - UDK_ETHER_HDR_LEN) - UDK_ETHER_CRC_LEN) /* Ethernet MTU. */
#define UDK_ETHER_MIN_MTU   68    /* Minimum MTU for IPv4 packets, see RFC 791. */

#define UDK_PKT_TX_TCP_SEG (1ULL << 50)
#define UDK_IPV4_HDR_IHL_MASK 0x0f /* IP header length mask for version_ihl field */
#define UDK_IPV4_IHL_MULTIPLIER 4  /* IHL field defines header length in number of 4-byte word */
#define UDK_ETHER_ADDR_ALIGN 2

struct udk_ether_addr {
    uint8_t addr_bytes[UDK_ETHER_ADDR_LEN];
} udk_aligned(UDK_ETHER_ADDR_ALIGN);

#define UDK_ETHER_LOCAL_ADMIN_ADDR 0x02 /* Locally assigned Eth. address. */

struct udk_ipv4_hdr {
    uint8_t version_ihl;
    uint8_t type_of_service;
    uint16_t total_length;
    uint16_t packet_id;
    uint16_t fragment_offset;
    uint8_t time_to_live;
    uint8_t next_proto_id;
    uint16_t hdr_checksum;
    uint32_t src_addr;
    uint32_t dst_addr;
} udk_packed;

struct udk_ipv4_psd_header {
    uint32_t src_addr; /* IP address of source host. */
    uint32_t dst_addr; /* IP address of destination host. */
    uint8_t zero;      /* zero. */
    uint8_t proto;     /* L4 protocol type. */
    uint16_t len;      /* L4 length. */
};

/*
 * Check if an Ethernet address is filled with 0.
 */
static inline int udk_is_zero_ether_addr(const struct udk_ether_addr *ea)
{
    const uint16_t *word = (const uint16_t *)ea;

    return (word[0] | word[1] | word[2]) == 0; /* ether addr 6Byte = 2Byte * 3 */
}

/*
 * Check if two Ethernet addresses are same.
 */
static inline int udk_is_same_ether_addr(const struct udk_ether_addr *ea_a, const struct udk_ether_addr *ea_b)
{
    const uint16_t *word_a = (const uint16_t *)ea_a;
    const uint16_t *word_b = (const uint16_t *)ea_b;

    return ((word_a[0] ^ word_b[0]) | (word_a[1] ^ word_b[1]) | (word_a[2] ^ word_b[2])) == 0; /* ether addr 6Byte = 2Byte * 3 */
}

/*
 * Copy an Ethernet address from src to dst.
 */
static inline void udk_ether_addr_copy(const struct udk_ether_addr *src_ea, struct udk_ether_addr *dst_ea)
{
    *dst_ea = *src_ea;
}

/*
 * Return the length of IPv4 header
 */
static inline uint8_t udk_ipv4_hdr_len(const struct udk_ipv4_hdr *ipv4_hdr)
{
    return (uint8_t)((ipv4_hdr->version_ihl & UDK_IPV4_HDR_IHL_MASK) * UDK_IPV4_IHL_MULTIPLIER);
}

#endif